﻿(*                  FnSharp              

   Base class library extensions and frameworks aimed 
   specifically at improving F# developers lives!

   Written by: Huw Simpson
   License: Microsoft Public License (Ms-PL) 
   Available at: http://fnsharp.codeplex.com

*)

namespace FnSharp.Bcl.Core

    open System

    [<RequireQualifiedAccess>]
    [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
    module String =
        /// Create a string from an array of characters.
        [<CompiledName("FromCharArray")>]
        let inline fromCharArray (chars:Char[]) =
            new String (chars)

        /// Removes all leading occurrences of whitespace characters.
        [<CompiledName("trimStart")>]
        let inline trimStart (str:string) =
            str.TrimStart [|' ';'\t';'\r';'\n'|]

        /// Removes all leading occurrences of a supplied array of characters.
        [<CompiledName("trimStart2")>]
        let inline trimStart2 chars (str:String) =
            str.TrimStart chars

        /// Removes all trailing occurrences of whitespace characters.
        [<CompiledName("trimEnd")>]
        let inline trimEnd (str:string) =
            str.TrimEnd [|' ';'\t';'\r';'\n'|]

        /// Removes all trailing occurrences of a supplied array of characters.
        [<CompiledName("trimEnd2")>]
        let inline trimEnd2 chars (str:String) =
            str.TrimEnd chars

        [<CompiledName("DefaultTo")>]
        let inline defaultTo isDefault defaultValue (str:string) =
            if isDefault str then defaultValue else str

        [<CompiledName("DefaultToNull")>]
        let inline defaultToNull defaultValue str =
            str |> defaultTo (fun x -> x = null) defaultValue

        [<CompiledName("DefaultToEmpty")>]
        let inline defaultToEmpty str = 
            str |> defaultTo (fun x -> x = null) String.Empty

        [<CompiledName("Skip")>]
        let inline skip n (s:String) =
            if n >= s.Length then String.Empty
            elif n < 1 then s
            else s.Substring(n)

        [<CompiledName("SkipLast")>]
        let inline skipLast n (s:String) =
            if n >= s.Length then String.Empty
            elif n < 1 then s
            else s.Substring(0, s.Length - n)

        [<CompiledName("Take")>]
        let inline take n (s:String) =
            if n >= s.Length then s
            elif n < 1 then String.Empty
            else s.Substring(0, n)

        [<CompiledName("TakeLast")>]
        let inline takeLast n (s:String) =
            if n >= s.Length then s
            elif n < 1 then String.Empty
            else s.Substring(s.Length - n, n)

        /// Concatenates all items in a string sequence, using the specified seperator between each item.
        [<CompiledName("Join")>]
        let inline join seperator (value:string seq) =
            String.Join(seperator, value)
            
        [<CompiledName("SplitOnChar")>]
        let inline splitOnChar (seperator:char list) (value:string) =
            value.Split (seperator |> List.toArray)

        [<CompiledName("SplitOnString")>]
        let inline splitOnString (seperator:string list) (value:string) =
            value.Split (seperator |> List.toArray, StringSplitOptions.None)

        [<CompiledName("Map")>]
        let inline map f (value:string) =
            f value

        [<CompiledName("MapChar")>]
        let inline mapChar f (value:string) =
            value.ToCharArray ()
            |> Array.map f
            |> fun arr -> new String (arr)

        [<CompiledName("GroupBy2")>]
        let inline groupBy2 (f: char -> string -> GroupingAction) (value:string) =
            value.ToCharArray ()
            |> Array.groupBy2 (fun c g -> f c (new String (g)))
            |> Array.map (fun arr -> new String (arr))            

        [<CompiledName("GroupBy")>]
        let inline groupBy f source =
            let fx a b = f a
            source |> groupBy2 fx  

        [<CompiledName("ToUpper")>]
        let inline toUpper (value:string) =
            value.ToUpper ()

        [<CompiledName("ToLower")>]
        let inline toLower (value:string) =
            value.ToLower ()

        [<CompiledName("Quote")>]
        let inline quote (value:string) =
            sprintf "\"%s\"" value

        [<CompiledName("ToPascalCase")>]
        let inline toPascalCase (value:string) =
            value
            |> groupBy (fun c -> if c = ' ' || c = '-' then YieldGroup (DiscardItem) else ContinueGroup (WithItem))
            |> Array.map (fun s -> (s |> take 1 |> map (toUpper)) + (s |> skip 1))
            |> join ""
            |> groupBy (fun c -> if c = '.' then YieldGroup (SeperateItem) else ContinueGroup (WithItem))
            |> Array.map (fun s -> (s |> take 1 |> map (toUpper)) + (s |> skip 1))
            |> join ""

        [<CompiledName("ToCamelCase")>]
        let inline toCamelCase (value:String) =
            value
            |> groupBy (fun c -> if c = ' ' || c = '-' then YieldGroup (DiscardItem) else ContinueGroup (WithItem))
            |> Array.map (fun s -> (s |> take 1 |> map (toUpper)) + (s |> skip 1))
            |> join ""
            |> groupBy (fun c -> if c = '.' then YieldGroup (SeperateItem) else ContinueGroup (WithItem))
            |> Array.map (fun s -> (s |> take 1 |> map (toLower)) + (s |> skip 1))
            |> join ""